package com.baitiaojun.system.admin.service.auth.impl;

import com.baitiaojun.common.constants.Constants;
import com.baitiaojun.common.constants.MsgConstants;
import com.baitiaojun.common.enums.impl.ServerExpEnums;
import com.baitiaojun.common.enums.impl.ServiceExpEnums;
import com.baitiaojun.common.exception.ServerException;
import com.baitiaojun.common.exception.ServiceException;
import com.baitiaojun.common.manager.AsyncManager;
import com.baitiaojun.common.utils.desensitize.RSAUtil;
import com.baitiaojun.common.utils.id.IdUtils;
import com.baitiaojun.common.utils.validation.ValidationUtils;
import com.baitiaojun.log.core.utils.LogUtils;
import com.baitiaojun.redis.core.support.RedisSupport;
import com.baitiaojun.security.core.domain.LoginUser;
import com.baitiaojun.security.core.service.AuthenticationService;
import com.baitiaojun.security.core.utils.SecurityUtils;
import com.baitiaojun.system.admin.controller.auth.req.AuthLoginReq;
import com.baitiaojun.system.admin.helper.CaptchaHelper;
import com.baitiaojun.system.admin.service.auth.AuthService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;

import javax.validation.constraints.NotEmpty;

@Slf4j
@Service
public class AuthServiceImpl implements AuthService {

    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private AuthenticationService authenticationService;

    @Autowired
    private CaptchaHelper captchaHelper;

    @Autowired
    private AsyncManager asyncManager;

    @Autowired
    private RedisSupport redisSupport;

    @Override
    public String login(AuthLoginReq authLoginDTO, @NotEmpty(message = "租户不能为空") String tenant) {
        //参数校验
        ValidationUtils.validation(tenant);

        //验证码校验
        captchaHelper.validateCaptcha(authLoginDTO.getCaptchaId(), authLoginDTO.getCaptcha());

        //进行用户登陆认证
        String decryptPass;
        try {
            decryptPass = RSAUtil.decrypt(authLoginDTO.getPassword());
        } catch (Exception e) {
            throw new ServerException(ServerExpEnums.EXP_PASS_DECRYPT_ERROR);
        }
        Authentication authenticate;
        UsernamePasswordAuthenticationToken authenticationToken
                = new UsernamePasswordAuthenticationToken(authLoginDTO.getUsername(), decryptPass);
        try {
            //该方法调用UserInfoServiceImpl的loadUserByUsername方法进行用户信息查询, 以及用户密码认证
            authenticate = authenticationManager.authenticate(authenticationToken);
        } catch (BadCredentialsException e) {
            ServiceExpEnums serviceExpEnums = ServiceExpEnums.EXP_LOGIN_USER_BAD_CREDENTIALS;
            log.warn(serviceExpEnums.getName() + "'" + authLoginDTO.getUsername() + "'");
            throw new ServerException(serviceExpEnums.getCode(), serviceExpEnums.getName());
        } catch (ServiceException e) {
            throw new ServerException(e.getCode(), e.getMessage());
        }

        //获取登陆人信息
        LoginUser loginUser = (LoginUser) authenticate.getPrincipal();

        //生成JWT
        String uuid = IdUtils.fastSimpleUUID();
        String token = authenticationService.createToken(uuid);

        //用户信息存入缓存
        loginUser.setTenant(tenant);
        loginUser.setLoginTime(System.currentTimeMillis());
        loginUser.setToken(uuid); //此用户唯一标识
        loginUser.setUserId(loginUser.getUserInfo().getUserId());
        authenticationService.addLoginUser(loginUser);

        //日志记录
        asyncManager.execute(
                LogUtils.recordLoginLog(tenant, loginUser.getUsername(), Constants.LOGIN_SUCCESS, MsgConstants.LOGIN_SUCCESS_MSG)
        );
        return token;

    }

    @Override
    public void logout() {
        LoginUser loginUser = SecurityUtils.getLoginUser();
        if (loginUser != null) {
            //清除缓存
            redisSupport.delete(Constants.LOGIN_TOKEN_KEY, loginUser.getToken());
            //日志记录
            asyncManager.execute(
                    LogUtils.recordLoginLog(loginUser.getTenant(), loginUser.getUsername(), Constants.LOGOUT, MsgConstants.LOGOUT_MSG)
            );
        }
    }
}
